//==================================================================================================
/**
  EVE - Expressive Vector Engine
  Copyright : EVE Project Contributors
  SPDX-License-Identifier: BSL-1.0
**/
//==================================================================================================
#include "test.hpp"

#include <eve/module/core.hpp>
#include <eve/module/special.hpp>
#include <cmath>

#if __has_include(<boost/math/special_functions/gamma.hpp>)
#include <boost/math/special_functions/gamma.hpp>
#define EVE_HAS_BOOST
#endif

#if defined(EVE_HAS_BOOST)
//==================================================================================================
// Types tests
//==================================================================================================
TTS_CASE_TPL("Check return types of gamma_p", eve::test::simd::ieee_reals)
<typename T>(tts::type<T>)
{
  using v_t = eve::element_type_t<T>;

  TTS_EXPR_IS(eve::gamma_p(T(), T()), T);
  TTS_EXPR_IS(eve::gamma_p(T(), v_t()), T);
  TTS_EXPR_IS(eve::gamma_p(v_t(), T()), T);
  TTS_EXPR_IS(eve::gamma_p(v_t(), v_t()), v_t);
};

//==================================================================================================
// gamma_p  tests
//==================================================================================================
TTS_CASE_WITH("Check behavior of gamma_p on wide",
              eve::test::simd::ieee_reals // but there is a flaw in boost_gamma_p for double
              ,
              tts::generate(tts::randoms(1.0, 100.0), tts::randoms(1.0, 5.0)))
<typename T>(T const& a0, T const& a1)
{
  using v_t = eve::element_type_t<T>;
  using eve::gamma_p;
  TTS_RELATIVE_EQUAL(eve::gamma_p(a0, a1),
                     tts::map([&](auto e, auto f) -> v_t { return boost::math::gamma_p(f, e); }, a0, a1),
                     1e-3);

  if constexpr( eve::platform::supports_invalids )
  {
    TTS_IEEE_EQUAL(gamma_p(eve::nan(eve::as<T>()), T(1)), eve::nan(eve::as<T>()));
    TTS_IEEE_EQUAL(gamma_p(eve::inf(eve::as<T>()), T(1)), T(1));
  }
  TTS_ULP_EQUAL(gamma_p(T(0.0), T(1.0)), T(0), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(1.0), T(1.0)), eve::oneminus(eve::exp(T(-1))), 1);
  TTS_ULP_EQUAL(gamma_p(T(0.0), T(0.0)), T(1), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(0.25), T(1.0)), T(2.211992169285951e-01), 1);
  TTS_ULP_EQUAL(gamma_p(T(0.25), T(0.25)), T(7.436779447314609e-01), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(1.0), T(0.25)), T(9.320788679898916e-01), 5.5);
  TTS_ULP_EQUAL(gamma_p(T(0.25), T(2.0)), T(2.649902116074387e-02), 5.5);
  TTS_ULP_EQUAL(gamma_p(T(0.25), T(2.25)), T(1.460405690340118e-02), 1.5);
  TTS_ULP_EQUAL(gamma_p(T(1.0), T(2.25)), T(2.015171022500710e-01), 3.5);
  TTS_ULP_EQUAL(gamma_p(T(10.25), T(2.0)), T(9.996022281154329e-01), 2);
  TTS_ULP_EQUAL(gamma_p(T(10.0), T(2.25)), T(9.991962820874968e-01), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(10.25), T(2.25)), T(9.993562658346851e-01), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(11.0), T(2.25)), T(9.996704204992496e-01), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(50.0), T(2.25)), T(1), 0);
  TTS_ULP_EQUAL(gamma_p(T(10.0), T(4)), T(9.896639493240743e-01), 0.5);
  TTS_ULP_EQUAL(gamma_p(T(10.1), T(4)), T(9.903947030847022e-01), 0.5);
};


//==================================================================================================
// Tests for masked gamma_p
//==================================================================================================
TTS_CASE_WITH("Check behavior of eve::masked(eve::gamma_p)(eve::wide)",
              eve::test::simd::ieee_reals,
              tts::generate(tts::randoms(1.0, 100.0),
                            tts::randoms(1.0, 5.0),
                            tts::logicals(0, 3)))
<typename T, typename M>(T const& a0,
                         T const& a1,
                         M const& mask)
{
  TTS_IEEE_EQUAL(eve::gamma_p[mask](a0, a1),
            eve::if_else(mask, eve::gamma_p(a0, a1), a0));
};
#else
TTS_CASE("Check return types of gamma_p")
{
  TTS_PASS("SKipping due to no reference available");
};
#endif

